package com.kcht.sylyg08.service;

import com.kcht.adp.common.ThreadManager;
import com.kcht.adp.common.config.ConfigurationValue;
import com.kcht.adp.common.log.Logger;
import com.kcht.sylyg08.dao.IAntDao;
import com.kcht.sylyg08.dao.ICommandPerDao;
import com.kcht.sylyg08.service.model.*;
import com.kcth.sylyg08.entity.Ant;
import com.kcth.sylyg08.entity.CommandPer;
import com.kcth.sylyg08.entity.ReportStatus;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;

import javax.annotation.PostConstruct;
import javax.ejb.Singleton;
import javax.ejb.Startup;
import javax.inject.Inject;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.core.Response;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 大屎山
 */
@Singleton
@Startup
public class TagRecvBufferHandler {
    @Inject
    private Logger logger;

    @Inject
    private ThreadManager threadManager;

    @Inject
    private IAntDao antDao;


    @Inject
    @ConfigurationValue
    private String heardBeatUri;//发送心跳地址

    @Inject
    private IReportToMes reportToMes;

    @Inject
    @ConfigurationValue
    private String FilterSwitch;

    /**
     * 启动时运行，从TagRecvBuffer中取出，处理。
     */

    private final Map<String, TagInfoTimer> commandMap = new ConcurrentHashMap<>();
    /**
     * 特殊情况
     */
    private final Map<String, TagInfoStateful> exceptionalCase = new ConcurrentHashMap<>();
    @Inject
    @ConfigurationValue
    private String exceptionalCaseTime;

    /**
     * 用于过滤，即工位只上报一次进出,除了180
     */
    private Map<String, String> stations = new ConcurrentHashMap<>();

    /**
     * 过滤了的信息
     */
    private final Map<String, FilterTagInfoTimer> filterTagMap = new ConcurrentHashMap<>();
    @Inject
    private ICommandPerDao commandPerDao;

    private final Map<String, Integer> op90Count = new HashMap<>();

    /**不过滤名单*/
    private final Set<String> notFilterSet=new HashSet<String>(){{
        add("OP180");add("RegressionTest");add("OP150");add("OP160");add("OP147");
    }};
    @PostConstruct
    private void init() {
        /** 将任务加入线程池 */
        threadManager.post(() -> {
            long timeFilter = Long.parseLong(FilterSwitch);
            while (true) {
                try {
                    TagInfo tagInfo = TagRecvBuffer.fetchTag();
                    /**
                     * 先修改标签时间
                     * 如果是首次进入，则在map中添加记录
                     * 如果不是首次进入，则直接替换map中的记录
                     */
                    {
                        if ("RegressionTest".equals(tagInfo.getDiscoverLocation().get("IP"))) {
                            logger.info(tagInfo.getEpc() + "--->LeaveQueue");
                        } else {
                            Ant ant = antDao.selectAnt(tagInfo.getDiscoverLocation().get("IP"), tagInfo.getDiscoverLocation().get("Ant"));
                            logger.info("发现标签  --->  " + ant.getLocation() + ", EPC: " + tagInfo.getEpc() + "，时间: " + tagInfo.getDiscoverTime());
                        }
                    }
                    //由于硬件设备时间错误，所以每次读到的标签都要先修改时间
//                            tagInfo.setDiscoverTime(new Date(System.currentTimeMillis()));
                    /**
                     * 软件层面解决特殊情况的补丁
                     * 如果到时候改装硬件而不需要了，可以直接删除
                     * */
                    {
                        Ant ant = antDao.selectAnt(tagInfo.getDiscoverLocation().get("IP"), tagInfo.getDiscoverLocation().get("Ant"));

                        /**
                         * 软件判断误读
                         * */
                               /* {
                                    if(ant.getLocation().equals("OP90")){
                                        String epc=tagInfo.getEpc();
                                        Integer count=op90Count.get(tagInfo.getEpc());
                                        if(count==null){
                                            op90Count.put(epc,1);
                                        }else{
                                            if(count<3){
                                                count++;
                                                op90Count.put(epc,count);
                                                logger.info("TagHandler  ---> 标签被过滤" + ant.getLocation() + ", EPC：" + tagInfo.getEpc());
                                                continue;
                                            }else{
                                                op90Count.remove(epc);
                                            }
                                        }
                                    }
                                }*/
                        if (ant.getLocation().equals("OP170")) {
                            TagInfo systemTag = new TagInfo();
                            systemTag.setTagId(tagInfo.getTagId());
                            systemTag.setEpc(tagInfo.getEpc());
                            systemTag.setDiscoverLocation(tagInfo.getDiscoverLocation());
                            systemTag.setMap(tagInfo.getMap());
                            systemTag.setSightingCount(tagInfo.getSightingCount());
                            systemTag.setDiscoverTime(new Date());
                            TagInfoStateful old = exceptionalCase.get(systemTag.getEpc());
                            if (old == null) {
                                logger.info("特例：TagHandler  --->  标签进入" + ant.getLocation() + "，EPC：" + systemTag.getEpc());
                                TagInfoStateful tagInfoStateful = new TagInfoStateful(systemTag, TagInfoState.IN);
                                exceptionalCase.put(tagInfo.getEpc(), tagInfoStateful);
                                mergeTagInfoToCommandPer(tagInfo, "1");
                            } else if (old.getState().equals(TagInfoState.IN) && systemTag.getDiscoverTime().getTime() - old.getTagInfo().getDiscoverTime().getTime() < Long.parseLong(exceptionalCaseTime)) {
                                old.setTagInfo(systemTag);
                                exceptionalCase.replace(tagInfo.getEpc(), old);
                            } else if (old.getState().equals(TagInfoState.OUT) && systemTag.getDiscoverTime().getTime() - old.getTagInfo().getDiscoverTime().getTime() > Long.parseLong(exceptionalCaseTime)) {
                                logger.info("特例：TagHandler  --->  标签进入" + ant.getLocation() + "，EPC：" + tagInfo.getEpc());
                                TagInfoStateful tagInfoStateful = new TagInfoStateful(systemTag, TagInfoState.IN);
                                exceptionalCase.put(tagInfo.getEpc(), tagInfoStateful);
                                mergeTagInfoToCommandPer(tagInfo, "1");
                            } else if (old.getState().equals(TagInfoState.OUT) && systemTag.getDiscoverTime().getTime() - old.getTagInfo().getDiscoverTime().getTime() < Long.parseLong(exceptionalCaseTime)) {
                                old.setTagInfo(systemTag);
                                exceptionalCase.replace(tagInfo.getEpc(), old);
                            } else if (old.getState().equals(TagInfoState.IN) && systemTag.getDiscoverTime().getTime() - old.getTagInfo().getDiscoverTime().getTime() > Long.parseLong(exceptionalCaseTime)) {
                                logger.info("特例：TagHandler  --->  标签离开" + ant.getLocation() + "，EPC：" + tagInfo.getEpc());
                                TagInfoStateful tagInfoStateful = new TagInfoStateful(systemTag, TagInfoState.OUT);
                                exceptionalCase.put(tagInfo.getEpc(), tagInfoStateful);
                                mergeTagInfoToCommandPer(tagInfo, "2");
                            }
                            continue;
                        }
                    }

                    //如果是第一次发现改标签，就把这个标签添加到内存的map里，并执行一次上报
                    /*补丁1 当某个工位中有零件，但是传过来零件在其他工位时*/
                    {
                        synchronized (commandMap) {
                            TagInfoTimer objectA = commandMap.get(tagInfo.getEpc());
                            if (Objects.nonNull(objectA)) {
                                TagInfo cacheTagInfo = objectA.getTagInfo();
                                if (cacheTagInfo != null && !tagInfo.getDiscoverLocation().get("IP").equals(cacheTagInfo.getDiscoverLocation().get("IP"))) {
                                    Ant ant = antDao.selectAnt(cacheTagInfo.getDiscoverLocation().get("IP"), cacheTagInfo.getDiscoverLocation().get("Ant"));
                                    logger.info("补丁1：TagHandler  --->  标签离开" + ant.getLocation() + "，EPC：" + cacheTagInfo.getEpc());
                                    cacheTagInfo.setDiscoverTime(new Date());
                                    objectA.cancelTimer();
                                    commandMap.remove(cacheTagInfo.getEpc());
                                    mergeTagInfoToCommandPer(cacheTagInfo, "2");
                                }
                            }
                        }
                    }
                    /*补丁1*/
                    /*补丁2 当OP170中有在工位状态的零件，而传过来零件在别的工位时*/
                    {
                        TagInfoStateful exceptCacheTagInfo = exceptionalCase.get(tagInfo.getEpc());
                        if (exceptCacheTagInfo != null
                                && exceptCacheTagInfo.getState().equals(TagInfoState.IN)
                                && !tagInfo.getDiscoverLocation().get("IP").equals(exceptCacheTagInfo.getTagInfo().getDiscoverLocation().get("IP"))) {
                            logger.info("补丁2：TagHandler  --->  标签离开OP170" + "，EPC：" + tagInfo.getEpc());
                            exceptCacheTagInfo.setState(TagInfoState.OUT);
                            exceptionalCase.put(tagInfo.getEpc(), exceptCacheTagInfo);
                            mergeTagInfoToCommandPer(exceptCacheTagInfo.getTagInfo(), "2");
                        }
                    }
                    /*补丁2*/
                    synchronized (commandMap) {
                        if (commandMap.get(tagInfo.getEpc()) == null) {
                            Ant ant = antDao.selectAnt(tagInfo.getDiscoverLocation().get("IP"), tagInfo.getDiscoverLocation().get("Ant"));
                            if (!tagInfo.getEpc().equals(stations.get(ant.getLocation()))) {
                                if ("RegressionTest".equals(tagInfo.getDiscoverLocation().get("IP"))) {
                                    logger.info(tagInfo.getEpc() + "--->TagEnterStation");
                                } else {
                                    logger.info("TagHandler  --->  标签进入" + ant.getLocation() + "，EPC：" + tagInfo.getEpc());
                                }
                                commandMap.put(tagInfo.getEpc(), new TagInfoTimer(tagInfo, timeFilter, this));
                                if(!notFilterSet.contains(ant.getLocation())){
                                    stations.put(ant.getLocation(), tagInfo.getEpc());
                                }
                                mergeTagInfoToCommandPer(tagInfo, "1");
                            } else {
                                synchronized (filterTagMap) {
                                    if (filterTagMap.get(ant.getLocation()) == null) {
                                        logger.info("TagHandler  ---> 标签重复进入" + ant.getLocation() + ", EPC：" + tagInfo.getEpc());
                                        filterTagMap.put(ant.getLocation(), new FilterTagInfoTimer(tagInfo, timeFilter, this));
                                        saveCommandPer(tagInfo, "1");
                                    } else {
                                        filterTagMap.get(ant.getLocation()).updateTimer(tagInfo);
                                    }
                                }
                            }
                        } else {
                            //直接替换map中的记录
                            commandMap.get(tagInfo.getEpc()).updateTimer(tagInfo);
                        }
                    }
                    Thread.sleep(10);//防止过度占用锁
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });


        /**
         * 每1分钟发送一次心跳
         * */
        new Timer().schedule(new TimerTask() {
            @Override
            public void run() {
                long id=System.currentTimeMillis();
                logger.info("开始心跳任务,识别号--->"+id);
                try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
                    HttpGet get=new HttpGet(heardBeatUri);
                    get.setConfig(RequestConfig.custom()
                            .setConnectTimeout(4000)
                            .setSocketTimeout(4000)
                            .setConnectionRequestTimeout(4000)
                            .build());
                    try (CloseableHttpResponse execute = httpClient.execute(get)) {
                        int statusCode = execute.getStatusLine().getStatusCode();
                        logger.info("心跳任务接收正常,返回状态-->" + statusCode+",识别号--->"+id);
                    } catch (Exception e) {
                        logger.error("连接中断，心跳发送失败,识别号--->"+id);
                        e.printStackTrace();
                    }
                } catch (Exception e) {
                    logger.error("未知异常,识别号--->"+id);
                    e.printStackTrace();
                }
            }
        },10,60*1000);
    }

    public void leave(TagInfo tagInfo) {
        Ant ant = antDao.selectAnt(tagInfo.getDiscoverLocation().get("IP"), tagInfo.getDiscoverLocation().get("Ant"));
        synchronized (commandMap) {
            if ("RegressionTest".equals(tagInfo.getDiscoverLocation().get("IP"))) {
                logger.info(tagInfo.getEpc() + "--->ReportTagLeaveStation");
                tagInfo.setDiscoverTime(new Date());
                commandMap.remove(tagInfo.getEpc());
            } else {
                logger.info("TagHandler  --->  标签离开" + ant.getLocation() + "，EPC：" + tagInfo.getEpc());
                tagInfo.setDiscoverTime(new Date());
                commandMap.remove(tagInfo.getEpc());
                mergeTagInfoToCommandPer(tagInfo, "2");
//                /**
//                 * 补丁，OP150误读严重，容易出现一种状况
//                 * 即
//                 * 正常A到OP150,上报A进入,OP150=A;
//                 * 误读B到OP150,上报B进入，但其实此件不在150工位,OP150=B
//                 * 正常A离开,上报A离开,OP150=B
//                 * 误读B离开，上报B离开,OP150=B
//                 * 正常B到OP150,OP150=B，这次由于之前已经上报过一次，所以这次不上报，但这次才应该上报
//                 * 要么根据数据特性解决误读，要么每次离开epc和工位epc不一致，则清空工位epc
//                 * */
//                {
//                    String OP150="OP150";
//                    if(OP150.equals(ant.getLocation())){
//                        if(stations.get(OP150)!=null&&!tagInfo.getEpc().equals(stations.get(OP150))){
//                            logger.info("当前离开OP150的标签与OP150最后读到的标签不一致 --->离开的标签："+tagInfo.getEpc()+"，最后读到的标签"+stations.get(OP150));
//                            stations.remove(OP150);
//                        }
//                    }
//                }
            }
        }
    }

    public void reLeave(TagInfo tagInfo) {
        Ant ant = antDao.selectAnt(tagInfo.getDiscoverLocation().get("IP"), tagInfo.getDiscoverLocation().get("Ant"));
        synchronized (filterTagMap) {
            logger.info("TagHandler  --->  标签重复离开" + ant.getLocation() + "，EPC：" + tagInfo.getEpc());
            tagInfo.setDiscoverTime(new Date());
            filterTagMap.remove(ant.getLocation());
            saveCommandPer(tagInfo, "2");
        }
    }

    private void saveCommandPer(TagInfo tagInfo, String flag) {

        CommandPer commandPer = new CommandPer();
        commandPer.setSystemTime(new Date());
        commandPer.setEpcCode(tagInfo.getEpc());
        commandPer.setDatetime(new Date());
        commandPer.setReportStatus(ReportStatus.过滤);
        Ant ant = antDao.selectAnt(tagInfo.getDiscoverLocation().get("IP"), tagInfo.getDiscoverLocation().get("Ant"));
        if (null != ant) {
            commandPer.setStaCode(ant.getLocation());
        } else {
            commandPer.setStaCode("unknown");
        }
        commandPer.setCheckNum(1);
        commandPer.setFlag(flag);
        threadManager.post(()->{
            commandPerDao.save(commandPer);
        });
    }

    /**
     * 将标签信息转化为CommandPer实体类信息
     * 如果天线未注册，则返回字符串"unknown"
     * <p>
     * 都为异步上报，原因在于上层不稳定，可能会不返回信息而导致时间过长
     * 时间过长将会阻塞消费线程，且存储数据库也会拖延消费者线程，也应当异步保存
     */
    private void mergeTagInfoToCommandPer(TagInfo tagInfo, String flag) {

        CommandPer commandPer = new CommandPer();
        commandPer.setSystemTime(new Date());
        commandPer.setEpcCode(tagInfo.getEpc());
        commandPer.setDatetime(tagInfo.getDiscoverTime());
        commandPer.setReportStatus(ReportStatus.正在上报);
        Ant ant = antDao.selectAnt(tagInfo.getDiscoverLocation().get("IP"), tagInfo.getDiscoverLocation().get("Ant"));
        if (null != ant) {
            commandPer.setStaCode(ant.getLocation());
        } else {
            commandPer.setStaCode("unknown");
        }
        commandPer.setCheckNum(1);
        commandPer.setFlag(flag);
        reportToMes.asyncReport(commandPer);
    }
}
